﻿using System;
using System.Collections.Generic;

namespace Kindohm.KSynth.Library
{
    public class Oscillator : AttenuatorBase, ISampleMaker
    {
        double frequency;
        uint phaseAngleIncrement;
        uint phaseAngle = 0;
        double detune;
        double finalFrequency;

        public Oscillator()
        {
            this.PhaseShift = 0;
            this.Pan = 0;
            this.Detune = 0;
        }

        public double Frequency
        {
            set
            {
                frequency = value;
                finalFrequency = frequency + detune;
                this.UpdatePhaseAngleIncrement();
            }
            get
            {
                return frequency;
            }
        }

        public short Pan { get; set; }
        public uint PhaseShift { get; set; }

        public double Detune
        {
            get { return detune; }
            set
            {
                this.detune = value;
                finalFrequency = frequency + detune;
                this.UpdatePhaseAngleIncrement();
            }
        }

        void UpdatePhaseAngleIncrement()
        {
            //phase angle calculation and use of uint as constant
            //borrwed from Charles Petzold
            phaseAngleIncrement = (uint)(finalFrequency * uint.MaxValue / 44100);
        }

        public StereoSample GetSample()
        {
            ushort wholePhaseAngle = (ushort)((phaseAngle >> 16));
            short sample = this.WaveForm.GetSample(wholePhaseAngle);
            phaseAngle += phaseAngleIncrement + this.PhaseShift;

            StereoSample stereoSample = new StereoSample() { LeftSample = sample, RightSample = sample };

            if (this.Pan > 0)
            {
                //right. decrease left
                double percent = short.MaxValue / this.Pan;
                stereoSample.LeftSample = (short)(stereoSample.LeftSample - (short)((double)stereoSample.LeftSample * percent));
            }
            else if (this.Pan < 0)
            {
                //left. decrease right
                double percent = short.MinValue / this.Pan;
                stereoSample.RightSample = (short)(stereoSample.RightSample - (short)((double)stereoSample.RightSample * (percent)));
            }

            return this.Attenuate(stereoSample);
        }

        public IWaveForm WaveForm { get; set; }
    }
}
